<?php

/**
 * JPGraph v4.0.3
 */

namespace lemon\JpGraph\Graph;

use lemon\JpGraph\Plot;
use lemon\JpGraph\Text;
use lemon\JpGraph\Util;

/**
 * Class CCBPGraph
 * Utility class to create Critical Chain Buffer penetration charts.
 */
class CCBPGraph
{
	const TickStep   = 25;
	const YTitle	 = '% Buffer used';
	const XTitle	 = '% CC Completed';
	const NColorMaps = 2;
	private $graph;
	private $iWidth;
	private $iHeight;
	private $iPlots	= [];
	private $iXMin	 = -50;
	private $iXMax	 = 100;
	private $iYMin	 = -50;
	private $iYMax	 = 150;
	private $iColorInd = [
		[5, 75], /* Green */
		[25, 85], /* Yellow */
		[50, 100], ]; /* Red */
	private $iColorMap  = 0;
	private $iColorSpec = [
		['darkgreen:1.0', 'yellow:1.4', 'red:0.8', 'darkred:0.85'],
		['#c6e9af', '#ffeeaa', '#ffaaaa', '#de8787'], ];
	private $iMarginColor = ['darkgreen@0.7', 'darkgreen@0.9'];
	private $iSubTitle	= '';
	private $iTitle	   = 'CC Buffer penetration';

	/**
	 * Construct a new instance of CCBPGraph.
	 *
	 * @param int $aWidth
	 * @param int $aHeight
	 *
	 * @return CCBPGraph
	 */
	public function __construct($aWidth, $aHeight)
	{
		$this->iWidth  = $aWidth;
		$this->iHeight = $aHeight;
	}

	/**
	 * Set the title and subtitle for the graph.
	 *
	 * @param string $aTitle
	 * @param string $aSubTitle
	 */
	public function SetTitle($aTitle, $aSubTitle)
	{
		$this->iTitle	= $aTitle;
		$this->iSubTitle = $aSubTitle;
	}

	/**
	 * Set the x-axis min and max values.
	 *
	 * @param int $aMin
	 * @param int $aMax
	 */
	public function SetXMinMax($aMin, $aMax)
	{
		$this->iXMin = floor($aMin / CCBPGraph::TickStep) * CCBPGraph::TickStep;
		$this->iXMax = ceil($aMax / CCBPGraph::TickStep) * CCBPGraph::TickStep;
	}

	/**
	 * Specify what color map to use.
	 *
	 * @param int $aMap
	 */
	public function SetColorMap($aMap)
	{
		$this->iColorMap = $aMap % CCBPGraph::NColorMaps;
	}

	/**
	 * Set the y-axis min and max values.
	 *
	 * @param int $aMin
	 * @param int $aMax
	 */
	public function SetYMinMax($aMin, $aMax)
	{
		$this->iYMin = floor($aMin / CCBPGraph::TickStep) * CCBPGraph::TickStep;
		$this->iYMax = ceil($aMax / CCBPGraph::TickStep) * CCBPGraph::TickStep;
	}

	/**
	 * Set the specification of the color backgrounds and also the
	 * optional exact colors to be used.
	 *
	 * @param mixed $aSpec   An array of 3 1x2 arrays. Each array specify the
	 *					   color indication value at x=0 and x=max x in order to determine the slope
	 * @param mixed $aColors An array with four elements specifying the colors
	 *					   of each color indicator
	 */
	public function SetColorIndication(array $aSpec, array $aColors = null)
	{
		if (safe_count($aSpec) !== 3) {
			Util\JpGraphError::Raise('Specification of scale values for background indicators must be an array with three elements.');
		}
		$this->iColorInd = $aSpec;
		if ($aColors !== null) {
			if (is_array($aColors) && safe_count($aColors) == 4) {
				$this->iColorSpec = $aColors;
			} else {
				Util\JpGraphError::Raise('Color specification for background indication must have four colors.');
			}
		}
	}

	/**
	 * Construct the graph.
	 */
	private function Init()
	{
		// Setup limits for color indications
		$lowx   = $this->iXMin;
		$highx  = $this->iXMax;
		$lowy   = $this->iYMin;
		$highy  = $this->iYMax;
		$width  = $this->iWidth;
		$height = $this->iHeight;

		// Margins
		$lm = 50;
		$rm = 40;
		$tm = 60;
		$bm = 40;

		if ($width <= 300 || $height <= 250) {
			$labelsize = 8;
			$lm		= 25;
			$rm		= 25;
			$tm		= 45;
			$bm		= 25;
		} elseif ($width <= 450 || $height <= 300) {
			$labelsize = 8;
			$lm		= 30;
			$rm		= 30;
			$tm		= 50;
			$bm		= 30;
		} elseif ($width <= 600 || $height <= 400) {
			$labelsize = 9;
		} else {
			$labelsize = 11;
		}

		if ($this->iSubTitle == '') {
			$tm -= $labelsize + 4;
		}

		$graph = new Graph($width, $height);
		$graph->clearTheme();
		$graph->SetScale('intint', $lowy, $highy, $lowx, $highx);
		$graph->SetMargin($lm, $rm, $tm, $bm);
		$graph->SetMarginColor($this->iMarginColor[$this->iColorMap]);
		$graph->SetClipping();

		$graph->title->Set($this->iTitle);
		$graph->subtitle->Set($this->iSubTitle);

		$graph->title->SetFont(FF_ARIAL, FS_BOLD, $labelsize + 4);
		$graph->subtitle->SetFont(FF_ARIAL, FS_BOLD, $labelsize + 1);

		$graph->SetBox(true, 'black@0.3');

		$graph->xaxis->SetFont(FF_ARIAL, FS_BOLD, $labelsize);
		$graph->yaxis->SetFont(FF_ARIAL, FS_BOLD, $labelsize);

		$graph->xaxis->scale->ticks->Set(CCBPGraph::TickStep, CCBPGraph::TickStep);
		$graph->yaxis->scale->ticks->Set(CCBPGraph::TickStep, CCBPGraph::TickStep);

		$graph->xaxis->HideZeroLabel();
		$graph->yaxis->HideZeroLabel();

		$graph->xaxis->SetLabelFormatString('%d%%');
		$graph->yaxis->SetLabelFormatString('%d%%');

		// For the x-axis we adjust the color so labels on the left of the Y-axis are in black
		$n1 = floor(abs($this->iXMin / 25)) + 1;
		$n2 = floor($this->iXMax / 25);
		if ($this->iColorMap == 0) {
			$xlcolors = [];
			for ($i = 0; $i < $n1; ++$i) {
				$xlcolors[$i] = 'black';
			}
			for ($i = 0; $i < $n2; ++$i) {
				$xlcolors[$n1 + $i] = 'lightgray:1.5';
			}
			$graph->xaxis->SetColor('gray', $xlcolors);
			$graph->yaxis->SetColor('gray', 'lightgray:1.5');
		} else {
			$graph->xaxis->SetColor('darkgray', 'darkgray:0.8');
			$graph->yaxis->SetColor('darkgray', 'darkgray:0.8');
		}
		$graph->SetGridDepth(DEPTH_FRONT);
		$graph->ygrid->SetColor('gray@0.6');
		$graph->ygrid->SetLineStyle('dotted');

		$graph->ygrid->Show();

		$graph->xaxis->SetWeight(1);
		$graph->yaxis->SetWeight(1);

		$ytitle = new Text\Text(CCBPGraph::YTitle, floor($lm * .75), ($height - $tm - $bm) / 2 + $tm);
		#$ytitle->SetFont(FF_VERA,FS_BOLD,$labelsize+1);
		$ytitle->SetAlign('right', 'center');
		$ytitle->SetAngle(90);
		$graph->Add($ytitle);

		$xtitle = new Text\Text(CCBPGraph::XTitle, ($width - $lm - $rm) / 2 + $lm, $height - 10);
		#$xtitle->SetFont(FF_VERA,FS_BOLD,$labelsize);
		$xtitle->SetAlign('center', 'bottom');
		$graph->Add($xtitle);

		$df = 'D j:S M, Y';
		if ($width < 400) {
			$df = 'D j:S M';
		}

		$time = new Text\Text(date($df), $width - 10, $height - 10);
		$time->SetAlign('right', 'bottom');
		#$time->SetFont(FF_VERA,FS_NORMAL,$labelsize-1);
		$time->SetColor('darkgray');
		$graph->Add($time);

		// Use an accumulated fille line graph to create the colored bands

		$n = 3;
		for ($i = 0; $i < $n; ++$i) {
			$b		   = $this->iColorInd[$i][0];
			$k		   = ($this->iColorInd[$i][1] - $this->iColorInd[$i][0]) / $this->iXMax;
			$colarea[$i] = [[$lowx, $lowx * $k + $b], [$highx, $highx * $k + $b]];
		}
		$colarea[3] = [[$lowx, $highy], [$highx, $highy]];

		$cb = [];
		for ($i = 0; $i < 4; ++$i) {
			$cb[$i] = new Plot\LinePlot(
				[$colarea[$i][0][1], $colarea[$i][1][1]],
				[$colarea[$i][0][0], $colarea[$i][1][0]]
			);
			$cb[$i]->SetFillColor($this->iColorSpec[$this->iColorMap][$i]);
			$cb[$i]->SetFillFromYMin();
		}

		$graph->Add(array_slice(array_reverse($cb), 0, 4));
		$this->graph = $graph;
	}

	/**
	 * Add a line or scatter plot to the graph.
	 *
	 * @param mixed $aPlots
	 */
	public function Add($aPlots)
	{
		if (is_array($aPlots)) {
			$this->iPlots = array_merge($this->iPlots, $aPlots);
		} else {
			$this->iPlots[] = $aPlots;
		}
	}

	/**
	 * Stroke the graph back to the client or to a file.
	 *
	 * @param mixed $aFile
	 */
	public function Stroke($aFile = '')
	{
		$this->Init();
		if (safe_count($this->iPlots) > 0) {
			$this->graph->Add($this->iPlots);
		}
		$this->graph->Stroke($aFile);
	}
}
